// ===================================
// <copyright>
// This file is part of LABSMFController.
//
//    LABSMFController is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    LABSMFController is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with LABSMFController.  If not, see <http://www.gnu.org/licenses/>.
//    Copyright 2011 David Bakker
//
//</copyright>
// <author>David Bakker</author>
// <email>mailto:DavidFBakker@gmail.com</email>
// <created>Saturday, May 07, 2011</created>
// <lastedit>Saturday, May 07, 2011</lastedit>
// ===================================

using System;
using System.Ext;
using System.Threading;
using LABSMFController.LABS.Classses;
using LABSMFController.LABS.Devices;

namespace LABSMFController.LABS.Services
{
    public class PumpServiceClass : BeerService
    {
        public PumpDevice[] Pumps;
        private Thread[] _myThreads;
        private StepRunner[] _stepRunners;


        public void ResetAllPumps()
        {
            if (Pumps == null)
                SetupPumps();

            for (int c = 0; c < BeerDevices.MAXPumps; c++)
            {
                Pumps[c].TurnOff();
            }
        }

        public void SetupPumps()
        {
            Location.Setup();

            Pumps = new PumpDevice[BeerDevices.MAXPumps];
            for (int c = 0; c < BeerDevices.MAXPumps; c++)
            {
                switch (c)
                {
                    case 0:
                        Pumps[c] = new PumpDevice("Pump " + c, BeerDevices.Pump0PIN);
                        break;
                    case 1:
                        Pumps[c] = new PumpDevice("Pump " + c, BeerDevices.Pump1PIN);
                        break;
                }
            }
        }

        public string GetPumpsClientOutPut()
        {
            string ret = "HEADER:Pumps:" + Pumps[0].Header + ";";
            for (int c = 0; c < BeerDevices.MAXPumps; c++)
            {
                ret = ret + "\r\n" + Pumps[c].GetClientOutPut();
            }
            return ret;
        }

        public override void Configure()
        {
            Console.Write("Logging configure");


            _myThreads = new Thread[BeerDevices.MAXPumps];
            _stepRunners = new StepRunner[BeerDevices.MAXPumps];
        }

        public override void Start()
        {
            BeerProgram.Log("PumpService starting");
        }

        public override void Stop()
        {
            BeerProgram.Log("PumpService stoping");
        }

        public void StartStep(WaterMovementClass waterMovement)
        {
            waterMovement = WaterMovementUtil.GetWaterMovementClass(WaterMovementsENUM.SourceHLTDestinationMLT);


            BeerProgram.Log("PumpService starting step " + waterMovement.Description);


            //if (! pumpStep.ConflictsWith.Equals(""))
            //{
            //    foreach (var cw in pumpStep.ConflictsWith.Split(','))
            //    {
            //        int c = Convert.ToInt32(cw);
            //        if (_stepRunners[c] != null)
            //            if (_stepRunners[c].IsRunning)
            //                StopStep(c);
            //    }
            //}

            _stepRunners[waterMovement.PumpNumber] = new StepRunner(waterMovement);
            _myThreads[waterMovement.PumpNumber] = new Thread(_stepRunners[waterMovement.PumpNumber].Run);
            _myThreads[waterMovement.PumpNumber].Start();
        }

        public void StopStep(int pumpNumber)
        {
            if (pumpNumber < 0 || pumpNumber >= BeerDevices.MAXPumps) return;

            BeerProgram.Log("PumpService stopping step " + pumpNumber);
            if (_stepRunners[pumpNumber] == null) return;
            _stepRunners[pumpNumber].Stop();
            _myThreads[pumpNumber].Abort();
        }
    }

    internal class StepRunner
    {
        private readonly WaterMovementClass _waterMovement;
        private bool _isRunning;
        private DateTime startTime;

        public StepRunner(WaterMovementClass waterMovement)
        {
            _waterMovement = waterMovement;
            StepNum = _waterMovement.PumpNumber;
        }

        public bool Immediate { get; private set; }
        public int StepNum { get; private set; }

        private double SourceStartingGallons { get; set; }
        private double DestinationStartingGallons { get; set; }

        public bool IsRunning
        {
            get { return _isRunning; }
            private set
            {
                _isRunning = value;
                _waterMovement.IsActive = value;
            }
        }

        public void Run()
        {
            if (_waterMovement.IsActive) return;

            Immediate = true;

            _waterMovement.IsActive = true;

            if (_waterMovement.SourceBubblerID != -1)
            {
                SourceStartingGallons = BeerProgram.GetLevel(_waterMovement.SourceBubblerID);
            } else
            {
                BeerProgram.Log("BeerStep " + StepNum + " source bubbler id is not set");
                Stop();
                return;
            }

            if (SourceStartingGallons <= 0)
            {
                BeerProgram.Log("BeerStep " + StepNum + " source is too low");
                IsRunning = false;
                Stop();
                return;
            }

            if (_waterMovement.DestinationBubblerID != -1)
            {
                DestinationStartingGallons = BeerProgram.GetLevel(_waterMovement.DestinationBubblerID);
            }

            BeerProgram.Log("BeerStep " + StepNum + " starting valves");
            for (int c = 0; c < _waterMovement.ValveCount; c++)
            {
                _waterMovement.Valves[c].TurnOn();
            }

            BeerProgram.Log("BeerStep " + StepNum + " starting vvalves");
            for (int c = 0; c < _waterMovement.VValveCount; c++)
            {
                if (_waterMovement.VValves[c].IsThreeWay)
                {
                    if (_waterMovement.VValveLevel[c] == VValveDevices.VValves[c].ASwing)
                    {
                        _waterMovement.VValves[c].TurnToA();
                    }
                    else
                    {
                        _waterMovement.VValves[c].TurnToB();
                    }
                }
                else
                {
                    _waterMovement.VValves[c].TurnLevel(_waterMovement.VValveLevel[c], false);
                }
            }


            for (int c = 0; c < _waterMovement.VValveCount; c++)
            {
                while (_waterMovement.VValves[c].IsRunning)
                    Thread.Sleep(100);
            }


            //  Stall start 
            if (!Immediate) Thread.Sleep(4000);

            BeerProgram.Log("BeerStep " + StepNum + " starting pumps");

          
            BeerProgram.PumpService.Pumps[_waterMovement.PumpNumber].TurnOn();
        
            IsRunning = true;
            BeerProgram.Log("Step " + StepNum + " is monitoring");
            if (_waterMovement.DurationToRun != 0)
            {
                double d = _waterMovement.DurationToRun * 1000;
                Thread.Sleep(Convert.ToInt32(d.ToString()));
                Stop();
                return;
            }


            if (PumpStep.BubblerID != -1)
            {
                int badCount = 0;
                var id = PumpStep.BubblerID;
                double gl = BeerProgram.GetLevel(id);
                double vol = gl - StartingGallons;
                var gallons = PumpStep.BubblerGallons;

                int refreshRecords = Convert.ToInt32(Settings.GetSetting("RefreshRecords"));


                double oldval = vol;
                if (PumpStep.BubblerEmpty)
                {
                    while (gl >= gallons + .32)
                    {
                        gl = BeerProgram.GetLevel(id);

                        if (oldval > gl - .01)
                        {
                            ++badCount;
                        }
                        else
                        {
                            oldval = gl;
                        }

                        if (badCount > refreshRecords)
                        {
                            BeerProgram.Log("PANIC Step " + StepNum + " is slow");
                            break;
                        }

                        Thread.Sleep(100);
                    }
                }
                else
                {
                    while (vol < (gallons - .3))
                    {
                        gl = BeerProgram.GetLevel(id);
                        vol = gl - StartingGallons;

                        if (oldval > vol - 5)
                            ++badCount;

                        oldval = vol;
                        if (badCount > refreshRecords)
                        {
                            BeerProgram.Log("PANIC Step " + StepNum + " is slow filling");
                            break;
                        }

                        Thread.Sleep(100);
                    }
                }
                Stop();
                return;
            }
        }

        private bool CheckWaterLevels()
        {
            if ( _waterMovement.SourceBubblerGallonsToMove != 0)
            {
                var gl = BeerProgram.GetLevel(_waterMovement.SourceBubblerID);
                          
            }
            return true;
        }

        public void Stop()
        {
            if (!Immediate) Thread.Sleep(1000);
            BeerProgram.Log("BeerStep " + StepNum + " stoping pumps");
            for (int c = 0; c < PumpStep.PumpCount; c++)
            {
                PumpStep.Pumps[c].TurnOff();
            }

            if (!Immediate) Thread.Sleep(2000);

            BeerProgram.Log("BeerStep " + StepNum + " stoping valves");
            for (int c = 0; c < PumpStep.ValveCount; c++)
            {
                PumpStep.Valves[c].TurnOff();
            }
            IsRunning = false;
            _waterMovement.IsActive = false;
        }
    }
}